# -*- coding: utf-8 -*-
'''
使用符合本模块树规范来存储树形结构

'''
import shelve
import os
import uuid
import xml.dom.minidom as minidom


import pystore.core.framework.util as util
import pystore.core.framework.os2 as os2



class ITreeNode(object):
    '''
    要使用tree_db模块，必须保证存储的树节点实现此接口
    标示接口，不准备被继承
    '''

    def getDbAttrs(self):
        '''
        getDbAttrs() -> dict
        
         返回此节点所有需要持久化的属性,属性以键值对放置到dict中，
         并且应该保证属性的值应该是能用eval和repr逆转的:
            assert eval(repr([1,2])) == [1,2]  #[1,2]就是一个有效的属性值
        '''
    
    def getDbNodeType(self):
        '''
        getDbNodeType() -> object
        
         返回节点的类型，这个类型在从数据文件中读取节点时给客户程序做提示用,通常是枚举或str类型
         只要符合eval和repr逆转性就可以:
            assert eval(repr([1,2])) == [1,2]  #[1,2]就是一个有效的类型,只要你愿意
        '''
        
    
    def getDbParent(self):
        '''
        getDbParent() -> ITreeNode
        
         返回节点父节点
        '''
       

def open(dbPath):
    return TreeDb(dbPath)
    
class TreeDb(object):
    def __init__(self,dbPath):
        self.dbPath=dbPath
        os2.makeFileDirs(self.dbPath)
        self._db=shelve.open(self.dbPath)
 
    def load(self,loadNodeFunc):
        '''
        load(loadNodeFunc) -> ITreeNode
    
         返回从文件中读取的树的根节点
        args:
           loadNodeFunc  : loadNodeFunc(ITreeNode parent,dict attrs,object type) -> ITreeNode
        '''
        
        assert not self.isEmpty() , 'can not load empty db , before load, use isEmpry() check'


        def getPersistenceAttrs(id):
            result = self._db[id].copy()
            del result[_ID]
            del result[_PARENT_ID]
            del result[_NODE_TYPE]
            return result
        
        def loadNode(allAttrs):
            id       = allAttrs[_ID]
            parentId = allAttrs[_PARENT_ID]
            nodeType = allAttrs[_NODE_TYPE]
            
            if createdNodes.has_key(id) :
                return createdNodes[id]
            
            parent = None
            if id != _ROOT_ID :  # not root
                parent = loadNode(self._db[parentId])

            result = loadNodeFunc(parent,getPersistenceAttrs(id),nodeType)
            result.__adapter_dbId__ = id
            createdNodes[id] = result
            return result

        createdNodes = {}
        for id,allAttrs in self._db.items():
            loadNode(allAttrs)
        return createdNodes[_ROOT_ID]

    def save(self,n):
        '''
        save(ITreeNode node)
    
         保存一个node到指定文件,并且只保存此节点,必须保证父节点已经保存.
         必须保证parent节点已经存储，否则被认为是程序错误,也就是说树的存储应该是自上而下的,类似文件系统.
        '''
        isRoot = n.getDbParent() is None
        
        if not isRoot:
            assert self.__isDbNode(n.getDbParent()) , 'before saving %s,must save parent %s first' % (n, n.getDbParent())

        if not self.__isDbNode(n): #不是db节点的要变成db节点
            n.__adapter_dbId__ = _ROOT_ID if isRoot else str(uuid.uuid4()) 
        
        id = n.__adapter_dbId__
        parentId = _ROOT_ID if isRoot else n.getDbParent().__adapter_dbId__

        allAttrs = n.getDbAttrs().copy()
        addedAttrs={
                _PARENT_ID:parentId,
                _ID:id,
                _NODE_TYPE:n.getDbNodeType()
                } 
        allAttrs.update(addedAttrs)
        self._db[id]   = allAttrs

    def remove(self,node):
        del self._db[node.__adapter_dbId__]

    def close(self):
        self._db.close()
    
    def isEmpty(self):
        return not self._db.has_key(_ROOT_ID)
    
    def __isDbNode(self,node):
        return hasattr(node,'__adapter_dbId__')

    
#已经可以工作，但先放下，以后再说,因为有点脱离主题，完全是无聊所致
#def writeXml(node,dbPath):
#    dom = minidom.getDOMImplementation().createDocument(None, 'node', None)
#    rootElement =  dom.documentElement
#    
#    def setElement(node,element):
#        element.setAttribute(_NODE_TYPE,node.getDbNodeType())
#        for attrK,attrV in node.getDbAttrs().items():
#            element.setAttribute(attrK,attrV)
#    
#    def createElements(node,element):
#        for childNode in node.getDbChildren():
#            childElement=dom.createElement('node')
#            element.appendChild(childElement)
#            setElement(childNode,childElement)
#            createElements(childNode,childElement)
#            
#    setElement(node,rootElement)
#    createElements(node,rootElement)
#    
#    util.writeXml(dom, dbPath)


#持久化附加属性
_PARENT_ID='__addedDbAttr_parentId__'
_ID='__addedDbAttr_id__'
_NODE_TYPE='__addedDbAttr_nodeType__'

#根节点使用固定id
_ROOT_ID='root_node_id'  
